package loader

import (
	"bytes"
	"fmt"
	"iot-base/common/cache"
	_ "iot-base/common/ctrl"
	"iot-base/common/idsync"
	"iot-base/common/logger"
	"iot-base/common/notify"
	rc "iot-base/common/redis"
	"runtime"
	"strconv"
	"time"
)

type setMapInfo struct {
	id       string
	newValue *mapValue
}

type mapValue struct {
	value        interface{}
	isReturn     bool
	lastCallTime int64
}

var (
	thingsMap     map[string]*mapValue
	currentName   string
	refreshWaitCh = make(chan string)
	forceChan     = make(chan string)
	//setMapCh        = make(chan setMapInfo)
	eachFun         func(id string, thing *cache.ThingInfo, v interface{}, round int64) (interface{}, error)
	devChangedFun   func(tid string, pThing *cache.ThingInfo)
	inThreadRun     bool
	timeoutForTimer int
	funTimerBegin   func()
	funTimerEnd     func()
)

//SetTimeoutForTimer 设置定时器时间
func SetTimeoutForTimer(timeout int) {
	if timeout <= 1 {
		timeout = 1
	}
	timeoutForTimer = timeout
}

func SetDevChangedHandler(fun func(tid string, pThing *cache.ThingInfo)) {
	devChangedFun = fun
}

func init() {
	//refreshCh := make(chan string, 10)
	//thingsMap = make(map[string]interface{})
	//var count = 0
	var id = ""
	notify.SubMsg(false, "dirty.thing", &id, func(topic string, body interface{}) {
		fmt.Println("thing changed-------------")
		//count++
		idchanged, ok := body.(*string)

		if ok {
			id := *idchanged
			fmt.Println("thing changed------------eeeee-id:", id)
			refreshWaitCh <- id
		}
		fmt.Println("sub refresh-----------end-------")
	})
	notify.SubMsg(false, "forceData", &id, func(topic string, body interface{}) {
		idforce, ok := body.(*string)
		if ok && idforce != nil {
			forceChan <- *idforce
		}
	})

}

//SetTimerFun 设置timer相关回调，begin在定时器来临时执行，end在timer完成时回调
func SetTimerFun(begin func(), end func()) {
	funTimerBegin = begin
	funTimerEnd = end
}

//TellDMNotToCheckOnline 通知上层不对本库的设备进行在线状态检测，
//此时断线告警以库的断线告警来设置状态
func TellDMNotToCheckOnline() {
	rc.SAdd("notCheckOnline.path.", currentName)
}

var running = false

func Init(proname string) {
	currentName = proname
	loadByProtocolName()
}

//var modelID string
//SetParam 设置回调函数，本接口和GetDevs需要选择一个使用
func SetParam(inThread bool, proname string, timeout int,
	eachfun func(id string, thing *cache.ThingInfo,
		v interface{}, round int64) (interface{}, error)) {
	if currentName != proname {
		Init(proname)
	}

	//modelID = smodelID
	//timeoutForTimer = timeout

	//fmt.Println("***************eachfun***", eachfun)
	setForEachCallback(inThread, eachfun)

	SetTimeoutForTimer(timeout)
	running = true
	fmt.Println("begin************run")
	go run()

}

var defaultTIMEOUT = 10

//SetParamEx 使用这个接口时，loader会根据interval的设定来调用回调函数，
//如果没有设定interval，则使用defaultTimeout
func SetParamEx(inThread bool, proname string, defaultTimeout int,
	eachfun func(id string, thing *cache.ThingInfo,
		v interface{}, round int64) (interface{}, error)) {
	defaultTIMEOUT = defaultTimeout
	//timeoutForTimer = 1
	SetParam(inThread, proname, 1, eachfun)
}

//SubCtrlHandler 订阅控制
func SubCtrlHandler(fun notify.CTRCALLBACK) {
	if fun != nil {
		notify.SubCtrlCmd(currentName, fun)
	}
}
func GetDevCount() int {
	return len(thingsMap)
}

//GetDevs 获取设备信息
func GetDevs(proname string) []*cache.ThingInfo {
	if currentName != proname {
		currentName = proname
		loadByProtocolName()
		if running == false {
			go run()
		}

	}
	result := make([]*cache.ThingInfo, 0, len(thingsMap))
	for id := range thingsMap {
		info, _ := cache.GetThing(id)
		if info != nil {
			result = append(result, info)
		}
	}
	return result
}

//SetForEachCallback 设置每个合格设备的回调
func setForEachCallback(inThread bool, eachfun func(id string, thing *cache.ThingInfo, v interface{}, runCount int64) (interface{}, error)) {
	inThreadRun = inThread
	eachFun = eachfun
}
func printStack() {
	var buf [4096]byte
	n := runtime.Stack(buf[:], false)
	fmt.Printf("==> %s\n", string(buf[:n]))
	logger.SLog.Error("exit--------", string(buf[:n]))
}

func runEachFun(k string, t *cache.ThingInfo, v *mapValue, count int64, now int64) {
	if t.Thing.StatusUsed == 1 {
		v.isReturn = false
		defer func(v *mapValue) {
			v.isReturn = true
			v.lastCallTime = now
		}(v)
		nv, _ := eachFun(k, t, v.value, count)
		//fmt.Println("---------runfun---------and set isreturn")
		v.lastCallTime = now
		v.isReturn = true
		v.value = nv
		/*
			if err != nil {
				//last, _ := cache.GetLastAliveTime(k)
				//if now-last > 120 {
				//event.ClearOfflineAlarm(k)
				//}
				//cache.SetLastAliveTime(k, now)
				logger.SLog.Error(currentName+" runeachfun error ", err)
				}*/

	}

}

func runFun(k string, v *mapValue, t *cache.ThingInfo, now int64, count int64) {
	defer func() {
		if r := recover(); r != nil {
			printStack()
		}
	}()
	if timeoutForTimer == 1 {
		sinterval := t.GetThingParam("interval", "")
		var interval int = 0
		if sinterval != "" {
			interval, _ = strconv.Atoi(sinterval)
		}
		if interval <= 0 {
			interval = defaultTIMEOUT
		}
		if interval > 0 {
			if now-v.lastCallTime >= int64(interval) {
				runEachFun(k, t, v, count, now)
			}
		}
		//fmt.Println("************interval", interval, v.lastCallTime, now, timeoutForTimer)
	} else {
		runEachFun(k, t, v, count, now)
		//fmt.Println("******222******interval", timeoutForTimer)
	}
}

func eachRun() {
	runCount++
	//fmt.Println("!!!!!!!!!!!!!!!!!!!!!!eachRun-----0--------", eachFun)
	if eachFun != nil {
		if funTimerBegin != nil {
			funTimerBegin()
		}
		//fmt.Println("!!!!!!!!!!!!!!!!!!!!!!eachRun-----1--------")
		now := time.Now().Unix()
		//fmt.Println("!!!!!!!!!!!!!!!!!!!!!!eachRun-----2--------", thingsMap)
		for k, v := range thingsMap {
			t, err := cache.GetThing(k)
			if err == nil && t.Thing.ProtocolPath == currentName {
				//cache.SetLastAliveTime(k,now)
				if v.isReturn {

					if inThreadRun {
						go runFun(k, v, t, now, runCount)
					} else {
						runFun(k, v, t, now, runCount)
					}
				} else {
					fmt.Println("give up------------", k, v, v.isReturn)
				}
			} else {
				//
				if cache.IsErrorDeviceNotFound(err) {
					idsync.ForceSynID(k)
				} /*else {
					delete(thingsMap, k)
				}*/
				logger.SLog.Warn("currentName not right", currentName, err, k)
			}
		}
		if funTimerEnd != nil {
			funTimerEnd()
		}
	}
	//fmt.Println("!!!!!!!!!!!!!!!!!!!!!!eachRun-----end--------", eachFun)
}

type ChangedFun func(ctype string, id string)

var changeFun ChangedFun

//SetThingChangedCallback 设置数据变化借口
func SetThingChangedCallback(fun ChangedFun) {
	changeFun = fun
}
func RereshAll() {
	refreshWaitCh <- "refreshAll"
}

var chanForGetAllIds = make(chan []string)

// func getIDs(isForce bool) {
// 	var ids []string
// 	if isForce {
// 		ids = loadByProtocolName()

// 	} else {
// 		ids = make([]string, 0, len(thingsMap))
// 		for k, _ := range thingsMap {
// 			ids = append(ids, k)
// 		}

// 	}
// 	chanForGetAllIds <- ids
// }

// func GetIDs(force bool, callback func(ids []string)) {
// 	go func() {
// 		var ids []string
// 		if force {
// 			refreshWaitCh <- "forceId"

// 		} else {
// 			refreshWaitCh <- "getId"

// 		}
// 		ids = <-chanForGetAllIds
// 		callback(ids)

// 	}()

// }

func updateByID(id string) {
	//logger.SLog.Info("add one0--------------------", id, currentName)
	pInfo, err := cache.GetThing(id)
	//logger.SLog.Info("add one1--------------------", id, currentName, err)
	if err == nil && pInfo != nil {
		fmt.Println("add one-2-------------------", id, currentName, pInfo.Thing.ProtocolPath)
		if pInfo.Thing.ProtocolPath == currentName {
			fmt.Println("add one----3----------------")
			if devChangedFun != nil {
				devChangedFun(id, pInfo)
			}
			if changeFun != nil {
				changeFun("change", id)
			}
			if eachFun != nil {
				fmt.Println("begin------------------------", id)
				eachFun(id, pInfo, nil, 0)
				fmt.Println("end------------------------", id)
			}
			thingsMap[id] = &mapValue{
				isReturn: true,
			}
		} else {
			delete(thingsMap, id)
			if changeFun != nil {
				changeFun("delete", id)
			}
		}
	} else {
		if changeFun != nil {
			changeFun("delete", id)
		}
	}
}
func goID() uint64 {
	b := make([]byte, 64)
	b = b[:runtime.Stack(b, false)]
	b = bytes.TrimPrefix(b, []byte("goroutine "))
	b = b[:bytes.IndexByte(b, ' ')]
	n, _ := strconv.ParseUint(string(b), 10, 64)
	return n
}

var runCount int64 = 0

func run() {
	running = true
	go eachRun()

	timer := time.NewTimer(time.Duration(timeoutForTimer) * time.Second)
	for {

		//fmt.Println("begin select refresh------------------", goID())

		//fmt.Println("begin----------select-------")
		select {

		case cmd := <-refreshWaitCh:
			//fmt.Println("get refresh------------------")
			fmt.Println("begin----------alll-------", cmd)
			switch cmd {
			case "refreshAll":
				loadByProtocolName()
			// case "forceId":
			// 	getIDs(true)
			//case "getId":
			//getIDs(false)
			default:
				fmt.Println("begin------------")
				updateByID(cmd)
				fmt.Println("end------------")

			}
			fmt.Println("end----------alll-------")

		case id := <-forceChan:
			pInfo, err := cache.GetThing(id)
			if pInfo != nil && err == nil {
				if pInfo.Thing.ProtocolPath == currentName {
					if eachFun != nil {
						fmt.Println("-------2 begin---------")
						eachFun(id, pInfo, nil, 0)
						fmt.Println("-------2 begin--------2-")
					}
				}

			}
		case <-timer.C:
			//fmt.Println("************timer begins************")
			timer.Reset(time.Duration(timeoutForTimer) * time.Second)
			go eachRun()

			//runCount++
		}
	}
	//fmt.Println("get refresh---44444-----", &refreshWaitCh)
	//<-refreshWaitCh
	//fmt.Println("get refresh------------------")

}
